/*
 * AutomatoProduto.cpp
 *
 *  Created on: 24/10/2012
 *      Author: vagner
 */

#include "AutomatoProduto.h"

AutomatoProduto::AutomatoProduto() {
	// Do nothing

}

AutomatoProduto::AutomatoProduto(Automato* M1, Automato* M2) {

	int numEstadosM1, numEstadosM2;
	Item tempItem;
	this->M1 = M1;
	this->M2 = M2;
	this->numEstados = 0;
	this->sigma = M1->getAlfabeto();
	numEstadosM1 = this->M1->getNumEstados();
	numEstadosM2 = this->M2->getNumEstados();
	this->tabelaTransicao = new TabelaTransicao((numEstadosM1 * numEstadosM2),
			this->sigma.size());
	//Definido um valor padro para as celulas
	tempItem.valor = -1;
	this->tabelaTransicao->defineValorPadrao(tempItem);

	//Constroi os estados
	constroiEstadoProduto();
}

int AutomatoProduto::isInAutomatoProduto(
		const EstadoProduto& estadoProduto) const {

	int indexEstado = -1;
	std::vector<EstadoProduto>::const_iterator itProduto;

	for (int i = 0; i < (int) conjuntoEstados.size(); i++) {
		if (estadoProduto.estados == conjuntoEstados.at(i).estados) {
			indexEstado = i;
			break;
		}
	}
	return indexEstado;
}

void AutomatoProduto::insereTabelaTransicao(int & estadoOrigem, char simbolo,
		int & estadoDestino) {
	Item aux;
	int indexSimbolo = getIndexSimbolo(simbolo);
	aux.valor = estadoDestino;
	tabelaTransicao->insereItem(aux, estadoOrigem, indexSimbolo);
}

int AutomatoProduto::getIndexSimbolo(char& simbolo) const {
	int tamAlfabeto = sigma.size();
	Alfabeto::iterator it;
	for (int i = 0; i < tamAlfabeto; i++) {
		if (sigma.at(i) == simbolo) {
			return i;
		}
	}
	return -1;
}

void AutomatoProduto::constroiEstadoProduto(void) {

	EstadoProduto estadoAtual, estadoCandidato;
	int proxEstadoM1, proxEstadoM2;
	std::vector<EstadoProduto> conjuntoTemp;
	std::vector<char>::const_iterator itSimbolo;
	/*
	 * Precisa-se verificar se um dado estado está no automato produto
	 * caso não esteja a varivael abaixo recebera o valor -1.
	 * do contrário tera o valor do id do estado.
	 */
	int estaNoProduto;

	/*
	 * Recuperando os estados inciais dos automatos
	 */
	proxEstadoM1 = M1->getEstadoInicial();
	proxEstadoM2 = M2->getEstadoInicial();

	estadoAtual.idEstado = numEstados;
	estadoAtual.estados.first = proxEstadoM1;
	estadoAtual.estados.second = proxEstadoM2;

	/*
	 * Adiciona o estado inicial ao conjunto de estado do produto
	 * e também no conjunto temporário
	 */
	this->conjuntoEstados.push_back(estadoAtual);
	conjuntoTemp.push_back(estadoAtual);

	while (!conjuntoTemp.empty()) {

		//Retira um estado do conjunto temporario
		estadoAtual = conjuntoTemp.at(0);
		conjuntoTemp.erase(conjuntoTemp.begin());

		for (itSimbolo = sigma.begin(); itSimbolo != sigma.end(); itSimbolo++) {

			//Recupera próximos estados
			proxEstadoM1 = M1->funcaoTransicao(estadoAtual.estados.first,
					(*itSimbolo));
			proxEstadoM2 = M2->funcaoTransicao(estadoAtual.estados.second,
					(*itSimbolo));
			estadoCandidato.estados.first = proxEstadoM1;
			estadoCandidato.estados.second = proxEstadoM2;

			//Verifica se o estado candidato já esta no automato produto
			estaNoProduto = isInAutomatoProduto(estadoCandidato);

			if (estaNoProduto < 0 && (estadoAtual.estados != estadoCandidato.estados )) {
				//O estado não esta no Automato Produto
				estadoCandidato.idEstado = ++numEstados;
				this->conjuntoEstados.push_back(estadoCandidato);
				conjuntoTemp.push_back(estadoCandidato);

			} else {
				/*
				 * O estado já esta no automato produto
				 * desta forma o automato entrou em ciclo
				 */
				//Tratando ciclos
				estadoCandidato.idEstado = estaNoProduto;
			}

			//Adiciona uma transicao entre o estado atual e estado candidado
			insereTabelaTransicao(estadoAtual.idEstado, *itSimbolo,
					estadoCandidato.idEstado);
		}

	}
}

void AutomatoProduto::show() const {

	Item aux;
	std::vector<EstadoProduto>::const_iterator itEstados;

	std::cout << "Tabela de transição do automato Produto: " << std::endl;
	for (int i = 0; i < tabelaTransicao->getNumLinhas(); i++) {
		for (int j = 0; j < tabelaTransicao->getNumColunas(); j++) {
			tabelaTransicao->getItem(i, j, aux);
			if (aux.valor != -1) {
				std::cout << "(" << conjuntoEstados.at(aux.valor).estados.first
						<< "," << conjuntoEstados.at(aux.valor).estados.second
						<< ") | ";
			}
		}
		std::cout << std::endl;
	}
}

void AutomatoProduto::analisaLinguagens(){
	//(a)
	analisaDisjuncao();
	//(b)
	analisaSubconjuntoProprio(this->MODO_0);
	//(c)
	analisaSubconjuntoProprio(this->MODO_1);
	//(d)
	analisaSubconjunto(this->MODO_0);
	//(e)
	analisaSubconjunto(this->MODO_1);
	//(f)
	analisaIgualdade();
}

void AutomatoProduto::analisaDisjuncao(){

	bool resultadoAnalise = true;
	std::vector<EstadoProduto>::const_iterator itEstados;
	int estadoM1, estadoM2;
	std::vector<int> estadosPercorridos;
	std::vector<int>::const_iterator itInt;
	std::string contraExemplo;

	for (itEstados = conjuntoEstados.begin();
			itEstados != conjuntoEstados.end(); itEstados++) {

		estadoM1 = itEstados->estados.first;
		estadoM2 = itEstados->estados.second;

		if (this->M1->isEstadoFinal(estadoM1)
				&& this->M2->isEstadoFinal(estadoM2)) {
			//As lingua não são disjuntas
			resultadoAnalise = false;
			estadosPercorridos.push_back(itEstados->idEstado);
			break;
		} else {
			estadosPercorridos.push_back(itEstados->idEstado);
		}
	}

	if (resultadoAnalise == true) {
		//As linguagens sao disjuntas
		std::cout << "(a) Sim." << std::endl;
	} else{
		//gerando um contra-exemplo
		geraPalavra(contraExemplo,estadosPercorridos);
		std::cout << "(a) Não." << "Contra-exemplo: "<< contraExemplo<< std::endl;
	}

}

void AutomatoProduto::analisaSubconjuntoProprio(int modo){

	bool resultadoAnalise = false;
	std::vector<EstadoProduto>::const_iterator itEstados;
	int estadoM1, estadoM2;
	std::vector<int> estadosPercorridos;
	std::vector<int>::const_iterator itInt;
	std::string contraExemplo;

	switch (modo) {
	case MODO_0:
		for (itEstados = conjuntoEstados.begin();
				itEstados != conjuntoEstados.end(); itEstados++) {

			estadoM1 = itEstados->estados.first;
			estadoM2 = itEstados->estados.second;

			if (!(this->M1->isEstadoFinal(estadoM1))
					|| this->M2->isEstadoFinal(estadoM2)) {
				resultadoAnalise = true;
			} else {
				resultadoAnalise = false;
				estadosPercorridos.push_back(itEstados->idEstado);
				break;
			}
			estadosPercorridos.push_back(itEstados->idEstado);
		}
		if (resultadoAnalise == true) {
			std::cout << "(b) Sim." << std::endl;
		} else {
			geraPalavra(contraExemplo,estadosPercorridos);
			std::cout << "(b) Não. Contra-exemplo: "<< contraExemplo << std::endl;
		}
		break;
	case MODO_1:
		for (itEstados = conjuntoEstados.begin();
				itEstados != conjuntoEstados.end(); itEstados++) {

			estadoM1 = itEstados->estados.first;
			estadoM2 = itEstados->estados.second;

			if ((this->M1->isEstadoFinal(estadoM1)
					|| !(this->M2->isEstadoFinal(estadoM2)))) {
				resultadoAnalise = true;
			} else {
				resultadoAnalise = false;
				estadosPercorridos.push_back(itEstados->idEstado);
				break;
			}
			estadosPercorridos.push_back(itEstados->idEstado);
		}
		if (resultadoAnalise == true) {
			std::cout << "(c) Sim." << std::endl;
		} else {
			geraPalavra(contraExemplo,estadosPercorridos);
			std::cout << "(c) Não." << "Contra-exemplo: " << contraExemplo << std::endl;
		}
		break;
	default:
		break;
	}
}

void AutomatoProduto::analisaSubconjunto(int modo) {

	bool primeiraCondicao = false;
	bool segundaCondicao = false;
	std::vector<EstadoProduto>::const_iterator itEstados;
	int estadoM1, estadoM2;
	std::vector<int> estadosPercorridos;
	std::vector<int>::const_iterator itInt;
	std::string contraExemplo;

	switch (modo) {
	case MODO_0:
		for (itEstados = conjuntoEstados.begin();
				itEstados != conjuntoEstados.end(); itEstados++) {

			estadoM1 = itEstados->estados.first;
			estadoM2 = itEstados->estados.second;

			if (!(this->M1->isEstadoFinal(estadoM1))
					|| this->M2->isEstadoFinal(estadoM2)) {
				primeiraCondicao = true;
			} else {
				primeiraCondicao = false;
				estadosPercorridos.push_back(itEstados->idEstado);
				break;
			}


			if (segundaCondicao == false) {
				if (!(this->M1->isEstadoFinal(estadoM1))
						&& this->M2->isEstadoFinal(estadoM2)) {
					segundaCondicao = true;
				}
			}
			estadosPercorridos.push_back(itEstados->idEstado);
		}
		if (primeiraCondicao == true && segundaCondicao == true) {
			std::cout << "(d) Sim." << std::endl;
		} else {
			geraPalavra(contraExemplo,estadosPercorridos);
			std::cout << "(d) Não. Contra-exemplo: " << contraExemplo << std::endl;
		}
		break;
	case MODO_1:
		for (itEstados = conjuntoEstados.begin();
				itEstados != conjuntoEstados.end(); itEstados++) {

			estadoM1 = itEstados->estados.first;
			estadoM2 = itEstados->estados.second;

			if ((this->M1->isEstadoFinal(estadoM1))
					|| !(this->M2->isEstadoFinal(estadoM2)) ){
				primeiraCondicao = true;
			} else {
				primeiraCondicao = false;
				estadosPercorridos.push_back(itEstados->idEstado);
				break;
			}

			if (segundaCondicao == false) {
				if ( !(this->M2->isEstadoFinal(estadoM2)) && (this->M1->isEstadoFinal(estadoM1))){
					segundaCondicao = true;
				}
			}
			estadosPercorridos.push_back(itEstados->idEstado);
		}
		if (primeiraCondicao == true && segundaCondicao == true) {
			std::cout << "(e) Sim." << std::endl;
		} else {
			geraPalavra(contraExemplo,estadosPercorridos);
			std::cout << "(e) Não. Contra-exemplo: " << contraExemplo << std::endl;
		}
		break;
	default:
		break;
	}
}

void AutomatoProduto::analisaIgualdade(){

	bool resultadoAnalise;
	std::vector<EstadoProduto>::const_iterator itEstados;
	int estadoM1, estadoM2;
	std::vector<int> estadosPercorridos;
	std::vector<int>::const_iterator itInt;
	std::string contraExemplo;

	for (itEstados = conjuntoEstados.begin();
			itEstados != conjuntoEstados.end(); itEstados++) {

		estadoM1 = itEstados->estados.first;
		estadoM2 = itEstados->estados.second;

		if (((!(this->M1->isEstadoFinal(estadoM1))
				|| (this->M2->isEstadoFinal(estadoM2)))
				&& (!(this->M2->isEstadoFinal(estadoM2))
						|| this->M1->isEstadoFinal(estadoM1)))) {
			resultadoAnalise = true;
		} else {

			resultadoAnalise = false;
			estadosPercorridos.push_back(itEstados->idEstado);
			break;
		}
		estadosPercorridos.push_back(itEstados->idEstado);
	}
	if (resultadoAnalise == true) {
		std::cout << "(f) Sim." << std::endl;
	} else {
		geraPalavra(contraExemplo,estadosPercorridos);
		std::cout << "(f) Não. Contra exemplo: "<< contraExemplo << std::endl;
	}

}

void AutomatoProduto::geraPalavra(std::string & palavraGerada,
		const std::vector<int>& estadosPercorridos) {

	int tamAlfabeto = (int) sigma.size();
	std::vector<int>::const_iterator itEstados;
	Item auxItem;

	if (int(estadosPercorridos.size()) < 2) {
		palavraGerada.append("lambda");
	}else {
		for (itEstados = estadosPercorridos.begin();
				itEstados != (estadosPercorridos.end() - 1); itEstados++) {

			for (int j = 0; j < tamAlfabeto; j++) {
				tabelaTransicao->getItem((*itEstados), j, auxItem);
				if (auxItem.valor == *(itEstados + 1)) {
					/*
					 * Existe uma transicao entre o estado na posicao i da lista para
					 * o estado na posicão i+1, sobre o simbolo na posicao j do alfabeto
					 */
					palavraGerada.push_back(sigma.at(j));
					break;
				}
			}

		}
	}

}

bool AutomatoProduto::comparaConjuntos(std::vector<int> idEstados,std::vector<EstadoProduto>& estadosProdutos){
	bool analise = true;
	int tamIDEstados = (int) idEstados.size();
	int tamEstadosProduto = (int) idEstados.size();
	int i;

	if( tamIDEstados != tamEstadosProduto ){
		analise = false;
		return analise;
	}else{
		for(i = 0; i < tamIDEstados; i++){
			if(idEstados.at(i) != estadosProdutos.at(i).idEstado ){
				analise = false;
				break;
			}
		}
	}
	return analise;
}

AutomatoProduto::~AutomatoProduto() {
	delete tabelaTransicao;
}

